import { ESCesiumViewer } from "@czmSrc/ESCesiumViewer";
import { MultiPointsEditing } from "..";
import { Event, createProcessingFromAsyncFunc, extendClassProps, ReactivePropsToNativePropsAndChanged, step, Destroyable, track } from "xbsj-base";
import { PlaceEditing } from "@czmSrc/EditingSystem/base/PlaceEditing";
import { AddEditingPoint } from "./AddEditingPoint";
import { ESJVector3DArray } from "earthsdk3";

export class MultiPointsAppendEditing extends MultiPointsEditing {
    private _deletePreviousPointWhileAddingEvent = this.disposeVar(new Event());
    deletePreviousPointWhileAdding() { this._deletePreviousPointWhileAddingEvent.emit(); }
    public placeEditing!: PlaceEditing;

    override resetPositions(value?: ESJVector3DArray): boolean {
        if (!super.resetPositions(value)) return true;
        const toAddItems = value ? value.map(e => new AddEditingPoint(this, e)) : [];
        this.editingPoints.splice(0, this.editingPoints.length, ...toAddItems);
        return true;
    }
    constructor(czmViewer: ESCesiumViewer) {
        super(czmViewer);
        if (!czmViewer.viewer) return;
        const placeEditing = this.placeEditing = this.ad(new PlaceEditing(czmViewer));
        placeEditing.enabled = false;
        this.currentProcess = this.disposeVar(createProcessingFromAsyncFunc(async cancelsManager => {
            cancelsManager.disposer.dispose(() => {
                (this.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`---editing canceled~~~`);
                this.status = 'None'
                this.enabled = false;
            });
            // 直接进入Adding状态
            (this.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`直接进入Adding状态`);
            this.status = 'Adding';
            await cancelsManager.promise(step(cancelsManager, async cancelsManager => {
                const d = cancelsManager.disposer;
                const adding = d.ad(this._addingEditingProcessing());
                d.dispose(() => `adding canceled!`);
                d.dispose(this.cancelEvent.disposableOn(() => adding.cancel()));
                await cancelsManager.promise(new Promise<void>(resolve => {
                    d.dispose(adding.completeEvent.disposableOnce(resolve))
                }));
            }));
        }));
        this.dispose(czmViewer.clickEvent.don(e => {
            // 右键按下时，删除上一个控制点
            if (e.pointerEvent?.button === 2) {
                this.deletePreviousPointWhileAdding();
            }
        }));
    }
    private _addingEditingProcessing() {
        const isMaxPoint = () => {
            if (this.maxPointsNum !== undefined) {
                if (this.editingPoints.length >= this.maxPointsNum) {
                    (this.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`impl.positionEditors.length >= impl.maxPointsNum`);
                }
                return this.editingPoints.length >= this.maxPointsNum
            }
        }
        const processing = createProcessingFromAsyncFunc(async (cancelsManager) => {
            let currentIndex = this.editingPoints.length;
            this.menuPoiEnabled = false;
            do {
                const next = await cancelsManager.promise(step(cancelsManager, async (cancelsManager) => {
                    let pa: PointAdding | undefined;
                    const allowPicking = !isMaxPoint(); // 如果已经达到最大点数，则不允许拾取
                    if (allowPicking) {
                        pa = cancelsManager.disposer.disposeVar(new PointAdding(this, currentIndex));
                    }
                    const pd = cancelsManager.disposer.disposeVar(new PreviousPointDeleting(this, currentIndex));
                    cancelsManager.disposer.dispose(this._deletePreviousPointWhileAddingEvent.disposableOn(() => pd.do()));
                    return await cancelsManager.promise(new Promise<boolean>(resolve => {
                        if (pa) {
                            cancelsManager.disposer.dispose(pa.overEvent.disposableOnce(success => {
                                if (success) {
                                    currentIndex++;
                                    resolve(true);
                                } else {
                                    resolve(false);
                                }
                            }));
                        }
                        cancelsManager.disposer.dispose(pd.overEvent.disposableOnce(success => {
                            if (success) {
                                currentIndex--;
                                resolve(true);
                            } else {
                                resolve(false);
                            }
                        }));
                        cancelsManager.disposer.dispose(this.cancelEvent.disposableOn(() => resolve(false)));
                    }));
                }));
                if (!next) break;
            } while (true);
        });
        processing.start();
        return processing;
    }
}
export namespace MultiPointsAppendEditing {
    export const createDefaultProps = () => ({
        ...MultiPointsEditing.createDefaultProps(),
    })
}
extendClassProps(MultiPointsAppendEditing.prototype, MultiPointsAppendEditing.createDefaultProps);
export interface MultiPointsAppendEditing extends ReactivePropsToNativePropsAndChanged<ReturnType<typeof MultiPointsAppendEditing.createDefaultProps>> { }


export class PointAdding extends Destroyable {
    private _overEvent = this.disposeVar(new Event<[boolean]>());
    get overEvent() { return this._overEvent; }

    constructor(multiPointsAppendEditing: MultiPointsAppendEditing, index: number) {
        super();

        const { placeEditing } = multiPointsAppendEditing;

        let currentPosEditor = new AddEditingPoint(multiPointsAppendEditing, undefined);
        multiPointsAppendEditing.editingPoints.splice(index, 0, currentPosEditor);
        const remove = () => {
            (multiPointsAppendEditing.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`delete currentPosEditor`);
            const n = multiPointsAppendEditing.editingPoints.indexOf(currentPosEditor);
            if (n === -1) {
                throw new Error(`currentPosEditor not found`);
            }
            multiPointsAppendEditing.editingPoints.splice(n, 1);
        };

        if (placeEditing.enabled ?? false) {
            console.error(`placeEditing.enabled is true!`);
            throw new Error(`placeEditing.enabled is true!`);
        }
        this.dispose(track([currentPosEditor, 'position'], [placeEditing, 'position']));

        placeEditing.enabled = true;

        let finished = false;
        const clear = () => {
            remove();
            this._overEvent.emit(false);
        }
        this.dispose(() => {
            placeEditing.enabled = false;
            !finished && clear();
        });
        this.dispose(placeEditing.overEvent.disposableOnce(() => {
            finished = true;
            if (currentPosEditor.position === undefined) {
                clear();
            } else {
                this._overEvent.emit(true);
            }
        }));

        (multiPointsAppendEditing.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`PointAdding creating! index(${index})`);
        this.dispose(() => {
            (multiPointsAppendEditing.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`PointAdding destroying! index(${index})`);
        });
    }
}

export function deletePreviousPoint(multiPointsAppendEditing: MultiPointsAppendEditing, currentIndex: number) {
    const posEditors = multiPointsAppendEditing.editingPoints;
    const previousIndex = currentIndex - 1;
    if (previousIndex < 0) {
        console.warn(`previousIndex < 0, cannot delete the point!`);
        return false;
    }
    posEditors.splice(previousIndex, 1);
    return true;
}

export class PreviousPointDeleting extends Destroyable {
    private _overEvent = this.disposeVar(new Event<[boolean]>());
    get overEvent() { return this._overEvent; }

    private _doEvent = this.disposeVar(new Event());
    do() { return this._doEvent.emit(); }

    constructor(multiPointsAppendEditing: MultiPointsAppendEditing, currentIndex: number) {
        super();
        this.dispose(this._doEvent.disposableOn(() => this._overEvent.emit(deletePreviousPoint(multiPointsAppendEditing, currentIndex))));
        (multiPointsAppendEditing.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`PreviousPointDeleting creating! currentIndex(${currentIndex})`);
        this.dispose(() => {
            (multiPointsAppendEditing.debug ?? MultiPointsAppendEditing.defaults.debug) && console.log(`PreviousPointDeleting destroying! currentIndex(${currentIndex})`);
        });
    }
}